﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.CampaignSystem.Siege.BesiegerCamp
// Assembly: TaleWorlds.CampaignSystem, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E85F8C15-4DF6-4E9C-A58A-29177E40D07A
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\bin\Win64_Shipping_Client\TaleWorlds.CampaignSystem.dll

using System;
using System.Collections.Generic;
using System.Linq;
using TaleWorlds.CampaignSystem.Map;
using TaleWorlds.CampaignSystem.MapEvents;
using TaleWorlds.CampaignSystem.Party;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.SaveSystem;
using TaleWorlds.SaveSystem.Load;

#nullable disable
namespace TaleWorlds.CampaignSystem.Siege
{
  public class BesiegerCamp : ISiegeEventSide
  {
    [SaveableField(8)]
    private readonly MBList<MobileParty> _besiegerParties;
    [SaveableField(9)]
    private MobileParty _leaderParty;
    [SaveableField(1)]
    private MBList<SiegeEvent.SiegeEngineMissile> _siegeEngineMissiles;

    internal static void AutoGeneratedStaticCollectObjectsBesiegerCamp(
      object o,
      List<object> collectedObjects)
    {
      ((BesiegerCamp) o).AutoGeneratedInstanceCollectObjects(collectedObjects);
    }

    protected virtual void AutoGeneratedInstanceCollectObjects(List<object> collectedObjects)
    {
      collectedObjects.Add((object) this._besiegerParties);
      collectedObjects.Add((object) this._leaderParty);
      collectedObjects.Add((object) this._siegeEngineMissiles);
      collectedObjects.Add((object) this.SiegeEvent);
      collectedObjects.Add((object) this.SiegeEngines);
      collectedObjects.Add((object) this.SiegeStrategy);
    }

    internal static object AutoGeneratedGetMemberValueSiegeEvent(object o)
    {
      return (object) ((BesiegerCamp) o).SiegeEvent;
    }

    internal static object AutoGeneratedGetMemberValueSiegeEngines(object o)
    {
      return (object) ((BesiegerCamp) o).SiegeEngines;
    }

    internal static object AutoGeneratedGetMemberValueSiegeStrategy(object o)
    {
      return (object) ((BesiegerCamp) o).SiegeStrategy;
    }

    internal static object AutoGeneratedGetMemberValueNumberOfTroopsKilledOnSide(object o)
    {
      return (object) ((BesiegerCamp) o).NumberOfTroopsKilledOnSide;
    }

    internal static object AutoGeneratedGetMemberValue_besiegerParties(object o)
    {
      return (object) ((BesiegerCamp) o)._besiegerParties;
    }

    internal static object AutoGeneratedGetMemberValue_leaderParty(object o)
    {
      return (object) ((BesiegerCamp) o)._leaderParty;
    }

    internal static object AutoGeneratedGetMemberValue_siegeEngineMissiles(object o)
    {
      return (object) ((BesiegerCamp) o)._siegeEngineMissiles;
    }

    [SaveableProperty(6)]
    public SiegeEvent SiegeEvent { get; private set; }

    [SaveableProperty(7)]
    public SiegeEvent.SiegeEnginesContainer SiegeEngines { get; private set; }

    public MobileParty LeaderParty => this._leaderParty;

    public IEnumerable<PartyBase> GetInvolvedPartiesForEventType(MapEvent.BattleTypes mapEventType = MapEvent.BattleTypes.Siege)
    {
      foreach (MobileParty besiegerParty in (List<MobileParty>) this._besiegerParties)
        yield return besiegerParty.Party;
    }

    public PartyBase GetNextInvolvedPartyForEventType(
      ref int partyIndex,
      MapEvent.BattleTypes mapEventType = MapEvent.BattleTypes.Siege)
    {
      ++partyIndex;
      return partyIndex >= this._besiegerParties.Count ? (PartyBase) null : this._besiegerParties[partyIndex].Party;
    }

    public bool HasInvolvedPartyForEventType(PartyBase party, MapEvent.BattleTypes mapEventType = MapEvent.BattleTypes.Siege)
    {
      foreach (MobileParty besiegerParty in (List<MobileParty>) this._besiegerParties)
      {
        if (besiegerParty.Party == party)
          return true;
      }
      return false;
    }

    public BattleSideEnum BattleSide => BattleSideEnum.Attacker;

    public MBReadOnlyList<SiegeEvent.SiegeEngineMissile> SiegeEngineMissiles
    {
      get => (MBReadOnlyList<SiegeEvent.SiegeEngineMissile>) this._siegeEngineMissiles;
    }

    [SaveableProperty(10)]
    public SiegeStrategy SiegeStrategy { get; private set; }

    [SaveableProperty(11)]
    public int NumberOfTroopsKilledOnSide { get; private set; }

    public BesiegerCamp(SiegeEvent siegeEvent)
    {
      this._besiegerParties = new MBList<MobileParty>();
      this.SiegeEvent = siegeEvent;
    }

    public bool IsBesiegerSideParty(MobileParty mobileParty)
    {
      return this.GetInvolvedPartiesForEventType(MapEvent.BattleTypes.Siege).Any<PartyBase>((Func<PartyBase, bool>) (t => t == mobileParty.Party || t.MobileParty.AttachedParties.Any<MobileParty>((Func<MobileParty, bool>) (k => k == mobileParty))));
    }

    private float PreparationProgress => this.SiegeEngines?.SiegePreparations?.Progress ?? 0.0f;

    public bool IsPreparationComplete => (double) this.PreparationProgress >= 1.0;

    public bool IsReadyToBesiege
    {
      get => this.IsPreparationComplete && this.StartingAssaultOnBesiegedSettlementIsLogical();
    }

    public void InitializeSiegeEventSide()
    {
      this.SiegeStrategy = DefaultSiegeStrategies.Custom;
      this.NumberOfTroopsKilledOnSide = 0;
      this.SiegeEngines = new SiegeEvent.SiegeEnginesContainer(BattleSideEnum.Attacker, new SiegeEvent.SiegeEngineConstructionProgress(DefaultSiegeEngineTypes.Preparations, this.PreparationProgress, (float) DefaultSiegeEngineTypes.Preparations.BaseHitPoints));
      this._siegeEngineMissiles = new MBList<SiegeEvent.SiegeEngineMissile>();
      this.SetPrebuiltSiegeEngines();
    }

    public void OnTroopsKilledOnSide(int killCount) => this.NumberOfTroopsKilledOnSide += killCount;

    public void SetSiegeStrategy(SiegeStrategy strategy) => this.SiegeStrategy = strategy;

    internal void AddSiegePartyInternal(MobileParty mobileParty)
    {
      this._besiegerParties.Add(mobileParty);
      if (this._besiegerParties.Count == 1)
        this.SiegeEvent.BesiegedSettlement.LastAttackerParty = mobileParty;
      this._leaderParty = Campaign.Current.Models.EncounterModel.GetLeaderOfSiegeEvent(this.SiegeEvent, BattleSideEnum.Attacker).PartyBelongedTo;
      this.ChangeSiegeStrategyIfNeeded();
    }

    internal void RemoveSiegePartyInternal(MobileParty mobileParty)
    {
      this._besiegerParties.Remove(mobileParty);
      if (mobileParty != null)
        this.OnPartyLeftSiege(mobileParty);
      if (this._besiegerParties.Count == 0)
      {
        this.SiegeEvent.FinalizeSiegeEvent();
      }
      else
      {
        if (this._leaderParty != mobileParty || this._leaderParty != null && this._leaderParty.MapEvent != null && this._leaderParty.MapEvent.State == MapEventState.WaitingRemoval)
          return;
        this._leaderParty = Campaign.Current.Models.EncounterModel.GetLeaderOfSiegeEvent(this.SiegeEvent, BattleSideEnum.Attacker)?.PartyBelongedTo;
        this.ChangeSiegeStrategyIfNeeded();
      }
    }

    public void RemoveAllSiegeParties()
    {
      MapEvent mapEvent = this.SiegeEvent.BesiegedSettlement.Party.MapEvent;
      if (mapEvent != null && mapEvent.IsSiegeAssault && !mapEvent.IsFinished)
        Debug.FailedAssert("RemoveAllParties called before mapEvent is cleared", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Siege\\BesiegerCamp.cs", nameof (RemoveAllSiegeParties), 168);
      while (this._besiegerParties.Count > 0)
        this._besiegerParties[0].BesiegerCamp = (BesiegerCamp) null;
    }

    private void OnPartyLeftSiege(MobileParty mobileParty)
    {
      if (mobileParty == MobileParty.MainParty && PlayerSiege.PlayerSiegeEvent != null)
        PlayerSiege.ClosePlayerSiege();
      if (Campaign.Current.Models.PartyImpairmentModel.CanGetDisorganized(mobileParty.Party))
        mobileParty.SetDisorganized(true);
      if (this._besiegerParties.Contains(MobileParty.MainParty) && (MobileParty.MainParty.MapEvent == null || !MobileParty.MainParty.MapEvent.HasWinner))
      {
        Campaign.Current.TimeControlMode = CampaignTimeControlMode.Stop;
        Campaign.Current.CurrentMenuContext?.Refresh();
      }
      mobileParty.Ai.SetMoveModeHold();
    }

    private void ChangeSiegeStrategyIfNeeded()
    {
      if (this.SiegeStrategy != DefaultSiegeStrategies.Custom || this._leaderParty.LeaderHero == Hero.MainHero)
        return;
      SiegeStrategy strategy = (SiegeStrategy) null;
      float num1 = float.MinValue;
      foreach (SiegeStrategy attackerStrategy in DefaultSiegeStrategies.AllAttackerStrategies)
      {
        float num2 = Campaign.Current.Models.SiegeEventModel.GetSiegeStrategyScore(this.SiegeEvent, BattleSideEnum.Attacker, attackerStrategy) * (float) (0.5 + 0.5 * (double) MBRandom.RandomFloat);
        if ((double) num2 > (double) num1)
        {
          num1 = num2;
          strategy = attackerStrategy;
        }
      }
      this.SetSiegeStrategy(strategy);
    }

    internal void SetSiegeCampPartyPosition(MobileParty mobileParty)
    {
      MatrixFrame[] besiegerCampPositions1 = this.SiegeEvent.BesiegedSettlement.Town.BesiegerCampPositions1;
      MatrixFrame[] besiegerCampPositions2 = this.SiegeEvent.BesiegedSettlement.Town.BesiegerCampPositions2;
      int num1 = 0;
      Vec2 vec2;
      bool flag1;
      do
      {
        int index = MBRandom.RandomInt(besiegerCampPositions1.Length);
        float randomFloat1 = MBRandom.RandomFloat;
        float randomFloat2 = MBRandom.RandomFloat;
        vec2 = besiegerCampPositions1[index].origin.AsVec2 + randomFloat1 * besiegerCampPositions1[index].rotation.s.AsVec2 + randomFloat2 * besiegerCampPositions1[index].rotation.f.AsVec2;
        flag1 = false;
        foreach (PartyBase partyBase in this.SiegeEvent.BesiegerCamp.GetInvolvedPartiesForEventType(MapEvent.BattleTypes.Siege))
        {
          if ((double) (vec2 - partyBase.MobileParty.VisualPosition2DWithoutError).LengthSquared < 0.25)
          {
            flag1 = true;
            break;
          }
        }
        ++num1;
      }
      while (flag1 && num1 < 20);
      if (num1 == 20 && besiegerCampPositions2 != null && besiegerCampPositions2.Length != 0)
      {
        int num2 = 0;
        bool flag2;
        do
        {
          int index = MBRandom.RandomInt(besiegerCampPositions2.Length);
          float randomFloat3 = MBRandom.RandomFloat;
          float randomFloat4 = MBRandom.RandomFloat;
          vec2 = besiegerCampPositions2[index].origin.AsVec2 + randomFloat3 * besiegerCampPositions2[index].rotation.s.AsVec2 + randomFloat4 * besiegerCampPositions2[index].rotation.f.AsVec2;
          flag2 = false;
          foreach (PartyBase partyBase in this.SiegeEvent.BesiegerCamp.GetInvolvedPartiesForEventType(MapEvent.BattleTypes.Siege))
          {
            if ((double) (vec2 - partyBase.MobileParty.VisualPosition2DWithoutError).LengthSquared < 0.25)
            {
              flag2 = true;
              break;
            }
          }
          ++num2;
        }
        while (flag2 && num2 < 20);
      }
      mobileParty.Position2D = vec2;
      mobileParty.Party.SetVisualAsDirty();
    }

    public void AddSiegeEngineMissile(SiegeEvent.SiegeEngineMissile missile)
    {
      this._siegeEngineMissiles.Add(missile);
    }

    public void RemoveDeprecatedMissiles()
    {
      this._siegeEngineMissiles.RemoveAll((Predicate<SiegeEvent.SiegeEngineMissile>) (missile => missile.CollisionTime.IsPast));
    }

    private bool StartingAssaultOnBesiegedSettlementIsLogical()
    {
      float num1 = 0.0f;
      float num2 = MobileParty.MainParty.CurrentSettlement == this.SiegeEvent.BesiegedSettlement ? 0.5f : 1f;
      foreach (PartyBase partyBase in this.SiegeEvent.BesiegedSettlement.GetInvolvedPartiesForEventType(MapEvent.BattleTypes.Siege))
      {
        if (partyBase.IsMobile && partyBase.MobileParty.CurrentSettlement == this.SiegeEvent.BesiegedSettlement && ((double) partyBase.MobileParty.Aggressiveness > 0.0099999997764825821 || partyBase.MobileParty.IsMilitia || partyBase.MobileParty.IsGarrison))
          num1 += num2 * partyBase.TotalStrength;
      }
      float num3 = 0.0f;
      LocatableSearchData<MobileParty> data = Campaign.Current.MobilePartyLocator.StartFindingLocatablesAroundPosition(this.SiegeEvent.BesiegedSettlement.Position2D, 10f);
      for (MobileParty nextLocatable = Campaign.Current.MobilePartyLocator.FindNextLocatable(ref data); nextLocatable != null; nextLocatable = Campaign.Current.MobilePartyLocator.FindNextLocatable(ref data))
      {
        if ((double) nextLocatable.Aggressiveness > 0.0 && nextLocatable.MapFaction == this.SiegeEvent.BesiegedSettlement.MapFaction && nextLocatable.CurrentSettlement == null)
          num3 += nextLocatable.Party.TotalStrength;
      }
      float num4 = 0.0f;
      foreach (PartyBase partyBase in this.SiegeEvent.BesiegerCamp.GetInvolvedPartiesForEventType(MapEvent.BattleTypes.Siege))
        num4 += partyBase.TotalStrength;
      float settlementAdvantage = Campaign.Current.Models.CombatSimulationModel.GetSettlementAdvantage(this.SiegeEvent.BesiegedSettlement);
      float equipmentProgress = Campaign.Current.Models.CombatSimulationModel.GetMaximumSiegeEquipmentProgress(this.SiegeEvent.BesiegedSettlement);
      float randomFloat = MBRandom.RandomFloat;
      float num5 = num4 / (num1 * MathF.Pow(settlementAdvantage, 0.67f));
      bool flag = false;
      float num6 = 0.9f;
      if ((double) num5 > (double) num6)
      {
        float b = 0.0f;
        float num7 = 0.0f;
        foreach (PartyBase partyBase in this.SiegeEvent.BesiegerCamp.GetInvolvedPartiesForEventType(MapEvent.BattleTypes.Siege))
        {
          b += partyBase.MobileParty.Food;
          num7 += -partyBase.MobileParty.FoodChange;
        }
        float num8 = MathF.Max(0.0f, b) / num7;
        float num9 = MathF.Min(1f, num3 / num4);
        float num10 = (double) num8 < 3.0 ? (float) ((1.0 + (3.0 - (double) num8) * (3.0 - (double) num8)) * 0.20000000298023224) : 0.0f;
        float num11 = (float) (((double) MathF.Min(4f, MathF.Max(num5, 1f)) - 1.0) * 0.20000000298023224);
        float totalStrength1 = this.SiegeEvent.BesiegerCamp.LeaderParty.MapFaction.TotalStrength;
        float totalStrength2 = this.SiegeEvent.BesiegedSettlement.MapFaction.TotalStrength;
        float num12 = (double) totalStrength1 < (double) totalStrength2 ? (float) ((1.0 - ((double) totalStrength1 + 0.0099999997764825821) / ((double) totalStrength2 + 0.0099999997764825821)) * 0.60000002384185791) : 0.0f;
        float y = MathF.Max(0.5f, 3f - num11 - num9 - num10 - num12);
        float num13 = MathF.Pow(settlementAdvantage, y);
        int ofEquipmentsBuilt = Campaign.Current.Models.CombatSimulationModel.GetNumberOfEquipmentsBuilt(this.SiegeEvent.BesiegedSettlement);
        float num14 = (MathF.Min(9f, num5) - num6) / num13 * (float) (((double) MathF.Min(2, ofEquipmentsBuilt) + 1.0) / 3.0) * (float) (1.0 - 0.6600000262260437 * ((double) equipmentProgress * (double) equipmentProgress));
        flag = (double) num1 == 0.0 || (double) randomFloat < (double) num14;
      }
      return flag;
    }

    private void SetPrebuiltSiegeEngines()
    {
      foreach (SiegeEngineType siegeEngine in Campaign.Current.Models.SiegeEventModel.GetPrebuiltSiegeEnginesOfSiegeCamp(this))
      {
        float siegeEngineHitPoints = Campaign.Current.Models.SiegeEventModel.GetSiegeEngineHitPoints(this.SiegeEvent, siegeEngine, BattleSideEnum.Attacker);
        SiegeEvent.SiegeEngineConstructionProgress constructionProgress = new SiegeEvent.SiegeEngineConstructionProgress(siegeEngine, 1f, siegeEngineHitPoints);
        this.SiegeEngines.AddPrebuiltEngineToReserve(constructionProgress);
        this.SiegeEvent.CreateSiegeObject(constructionProgress, this.SiegeEvent.GetSiegeEventSide(BattleSideEnum.Defender));
      }
    }

    private float GetDistanceBetweenRangedEngineAndWall(int rangedEngine, int wallIndex)
    {
      return MathF.Abs((float) rangedEngine * 1f - (float) (0.5 + 2.0 * (double) wallIndex)) + 2f;
    }

    private float PriorityCalculationForWalls(float distance) => 5f - distance;

    private void FindAttackableWallSectionWithHighestPriority(
      int attackerSlotIndex,
      SiegeEngineType siegeEngine,
      out int targetIndex,
      out float targetPriority)
    {
      targetIndex = -1;
      targetPriority = 0.0f;
      if (siegeEngine.IsAntiPersonnel)
        return;
      float num1 = 9999f;
      MBReadOnlyList<float> hitPointsRatioList = this.SiegeEvent.BesiegedSettlement.SettlementWallSectionHitPointsRatioList;
      for (int index = 0; index < hitPointsRatioList.Count; ++index)
      {
        if ((double) hitPointsRatioList[index] > 0.0)
        {
          float rangedEngineAndWall = this.GetDistanceBetweenRangedEngineAndWall(attackerSlotIndex, index);
          float num2 = this.PriorityCalculationForWalls(rangedEngineAndWall);
          if ((double) num2 > (double) targetPriority || (double) MathF.Abs(num2 - targetPriority) < 9.9999997473787516E-05 && (double) num1 > (double) rangedEngineAndWall)
          {
            targetIndex = index;
            targetPriority = num2;
            num1 = rangedEngineAndWall;
          }
        }
      }
    }

    public void BombardHitWalls(SiegeEngineType attackerEngineType, int wallIndex)
    {
      MBReadOnlyList<float> hitPointsRatioList = this.SiegeEvent.BesiegedSettlement.SettlementWallSectionHitPointsRatioList;
      if ((double) hitPointsRatioList[wallIndex] <= 0.0)
        return;
      float num = Campaign.Current.Models.SiegeEventModel.GetSiegeEngineDamage(this.SiegeEvent, BattleSideEnum.Attacker, attackerEngineType, SiegeBombardTargets.Wall) / this.SiegeEvent.BesiegedSettlement.MaxHitPointsOfOneWallSection;
      this.SiegeEvent.BesiegedSettlement.SetWallSectionHitPointsRatioAtIndex(wallIndex, MBMath.ClampFloat(hitPointsRatioList[wallIndex] - num, 0.0f, 1f));
      bool isWallCracked = (double) hitPointsRatioList[wallIndex] <= 0.0;
      if (isWallCracked)
        this.SiegeEvent.BesiegedSettlement.Party.SetVisualAsDirty();
      CampaignEventDispatcher.Instance.OnSiegeBombardmentWallHit(this.LeaderParty, this.SiegeEvent.BesiegedSettlement, BattleSideEnum.Attacker, attackerEngineType, isWallCracked);
    }

    public void GetAttackTarget(
      ISiegeEventSide siegeEventSide,
      SiegeEngineType siegeEngine,
      int siegeEngineSlot,
      out SiegeBombardTargets targetType,
      out int targetIndex)
    {
      targetType = SiegeBombardTargets.None;
      targetIndex = -1;
      int targetIndex1;
      float targetPriority1;
      siegeEventSide.SiegeEvent.FindAttackableRangedEngineWithHighestPriority((ISiegeEventSide) this, siegeEngineSlot, out targetIndex1, out targetPriority1);
      int targetIndex2;
      float targetPriority2;
      this.FindAttackableWallSectionWithHighestPriority(siegeEngineSlot, siegeEngine, out targetIndex2, out targetPriority2);
      if (targetIndex1 == -1 && targetIndex2 == -1)
        return;
      if (targetIndex1 == -1)
      {
        targetIndex = targetIndex2;
        targetType = SiegeBombardTargets.Wall;
      }
      else if (targetIndex2 == -1)
      {
        targetIndex = targetIndex1;
        targetType = SiegeBombardTargets.RangedEngines;
      }
      else if ((double) MBRandom.RandomFloat * (double) (targetPriority1 + targetPriority2) < (double) targetPriority1)
      {
        targetIndex = targetIndex1;
        targetType = SiegeBombardTargets.RangedEngines;
      }
      else
      {
        targetIndex = targetIndex2;
        targetType = SiegeBombardTargets.Wall;
      }
    }

    public void FinalizeSiegeEvent()
    {
      if (this.GetInvolvedPartiesForEventType(MapEvent.BattleTypes.Siege).IsEmpty<PartyBase>())
        return;
      this.RemoveAllSiegeParties();
    }

    [LateLoadInitializationCallback]
    private void OnLoad(MetaData metaData, ObjectLoadData objectLoadData)
    {
      if (this._leaderParty != null || !MBSaveLoad.IsUpdatingGameVersion || !(MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.2.0")))
        return;
      this._leaderParty = this._besiegerParties.Count > 0 ? this._besiegerParties[0] : (MobileParty) null;
    }
  }
}
